4.10. ORM
Разработчику
Аналитику
Тестировщику
Архитектору
Инженеру
ORM
Основные понятия
★ ORM – это программный подход или инструмент, который позволяет преобразовывать данные между объектной моделью программы (объекты, классы) и реляционной моделью базы данных (таблицы, строки, столбцы).
★ Объектная модель - в ООП данные представлены в виде объектов, которые имеют свойства (атрибуты) и поведение (методы).
★ Реляционная модель - в реляционных базах данных, данные хранятся в таблицах, где строки представляют записи, а столбцы - атрибуты.
Задача ORM - вывести формулу:
Объектная модель - Реляционная модель.
ORM выступает как переводчик между этими двумя парадигмами, позволяя программисту работать с базой данных через объекты, а не напрямую через SQL-запросы.
ORM предоставляет уровень абстракции, который скрывает сложности работы с базой данных. Это означает, что программист может сосредоточиться на логике приложения, а не на деталях взаимодействия с БД.
Абстракция ORM
★ Как работает абстракция ORM?
- Классы как таблицы:
- каждый класс в программе соответствует таблице в базе данных;
- свойства класса соответствуют столбцам таблицы.
- Объекты как строки:
- экземпляр класса (объект) представляет собой строку в таблице;
- значения свойств объекта соответствуют значениям в ячейках строки.
- Методы для операций с данными. ORM предоставляет готовые методы для выполнения CRUD-операций (Create, Read, Update, Delete), которые автоматически преобразуются в SQL-запросы.
- Отношения между объектами. Базы не просто называются реляционными - важна связь. И ORM поддерживает отношения между объектами (например, один-ко-многим, многие-к-другим), которые отражаются в связях между таблицами в базе данных.
Пример абстракции.
Прямой SQL-запрос был бы таким:
SELECT * FROM Users WHERE Age > 30
Абстракция в C#, через LINQ-синтаксис (позже изучим):
var users = (from user in dbContext.Users
where user.Age > 30
select user).ToList();
А если использовать Entity Framework (прокаченную ORM на C#), то:
var users = dbContext.Users.Where(user => user.Age > 30).ToList();
Конечно, мы ещё не изучали особенности языка C#, но здесь важно понять следующее при разборе примеров:
dbContext.Users— этоDbSet<User>, представляющий таблицуUsersв БД..Where(...)— метод LINQ, который преобразуется в SQL-условиеWHEREпод капотом.user => user.Age > 30— лямбда-выражение, заменяющее условиеWHERE Age > 30(анонимная функция, которая фильтрует записи);.ToList()– материализует запрос (выполняет SQL и возвращает список объектов).
Таким образом, если приводить соответствие:
| Абстракция в ORM (C#) | Эквивалент в SQL БД |
|---|---|
Класс Users | Таблица Users |
Свойства класса Users (Id, Name, Age) | Столбцы таблицы Users (Id, Name, Age) |
dbContext.Users | SELECT * FROM Users |
| .Where(user => user.Age > 30) | WHERE Age > 30 |
.Select(user => user.Name) | SELECT Name FROM Users |
users.ToList() | Выполнение запроса (Execute) |
Переменная users | Результат запроса SQL |
Методы Where, From, Select | Ключевые слова WHERE, FROM, SELECT |
| Объект user (экземпляр класса) | Запись (строка) в таблице |
Следовательно, в базе данных есть таблица Users:
| Id | Name | Address | |
|---|---|---|---|
| 1 | Иван Петров | ул. Ленина, 10 | ivan@mail.ru |
Представим, что создавалась она так:
CREATE TABLE Users (
Id INT PRIMARY KEY IDENTITY(1,1), -- Автоинкрементный первичный ключ
Name NVARCHAR(100) NOT NULL, -- Имя пользователя
Address NVARCHAR(200) NULL, -- Адрес (может быть NULL)
Email NVARCHAR(100) NOT NULL -- Email (уникальный)
);
В коде мы бы создали класс User, со следующим содержимым:
[Table("Users")]
public class User
{
[Key] // Указывает, что это первичный ключ
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] // Автоинкремент
public int Id { get; set; }
[Required] // NOT NULL в БД
[MaxLength(100)] // Ограничение длины (NVARCHAR(100))
public string Name { get; set; }
[MaxLength(200)] // NULL в БД (по умолчанию, если не Required)
public string? Address { get; set; } // "?" означает, что может быть null
[Required]
[MaxLength(100)]
[EmailAddress] // Проверка формата email
public string Email { get; set; }
}
По итогу мы получаем сопоставление:
| C# (Класс User) | SQL (Таблица Users) |
|---|---|
public int Id | Id INT PRIMARY KEY |
public string Name | Name NVARCHAR(100) |
public string? Address | Address NVARCHAR(200) NULL |
public string Email | Email NVARCHAR(100) |
В коде выше, конечно, добавили атрибуты вроде [Key], [Required] - они помогают ORM понять структуру БД. Но! Мы ещё не изучили C#, поэтому задача в вышеуказанном примере - на практике показать, что такое ORM.
Логическое представление
Вернёмся к теории.
ORM является логическим представлением, и позволяет работать с базой данных как с коллекцией объектов, а не как с набором таблиц. Как мы помним, это совсем разные структуры данных. ORM при этом скрывает детали реализации, и программисту не нужно знать, как именно организована структура базы данных (например, какие индексы используются или как выполняются запросы). ORM автоматически генерирует SQL-запросы на основе действий с объектами, что автоматизирует процесс.
Поэтому здесь работа идёт с объектами, а ORM сама заботится об SQL-запросах.
Реляционное представление подразумевает хранение данных в таблицах с чётко определённой схемой - строки и столбцы. ORM для реляционных БД преобразует таблицы в классы, строки в объекты, а столбцы в свойства объектов, а также поддерживает отношения между таблицами (например, внешние ключи) через связи между объектами.
Примеры ORM для реляционных БД:
- Hibernate (Java)
- Entity Framework (C#)
- SQLAlchemy (Python)
Но это не значит, что для нереляционных БД отсутствует ORM.
В нереляционном представлении, данные хранятся в более гибком формате (документы JSON, графы, ключ-значение). И для них тоже используется ORM. ORM адаптируется к специфике нереляционных баз данных, вместо таблиц используются коллекции, а вместо строк - документы.
Примеры ORM для нереляционных БД:
- Mongoose (для MongoDB)
- ODM (Object Document Mapping) — аналог ORM для документоориентированных БД.
В чём отличие между реляционным и нереляционным представлением в ORM?
| Характеристика | Реляционное представление | Нереляционное представление |
|---|---|---|
| Структура данных | Таблицы, строки, столбцы | Документы, ключ-значение, графы |
| Связи между данными | Через внешние ключи | Через ссылки или вложенные структуры |
| Гибкость схемы | Жесткая схема | Гибкая схема |
| Пример ORM | Hibernate, Entity Framework | Mongoose, ODM |